import * as boom from '@hapi/boom';

import { Post, PostId } from './model';
import { mainDB } from '../../infra/mysql';
import { RowDataPacket } from 'mysql2';
import { UserId } from '../user/model';

export interface PostRepository {
  selectByPostId(postId: PostId): Promise<Post | undefined>;
  selectByUserId(userId: UserId): Promise<Post[]>;
  countUserPosts(userId: UserId): Promise<number>;

  /**
   * 仅当 postId < 1 时插入
   *
   * @param post
   */
  insert(post: Post): Promise<PostId>;

  update(post: Post): Promise<void>;
}

class PostMySQLRepository implements PostRepository {
  async update(post: Post): Promise<void> {
    const sql = `
      update post
      set title   = ?,
          content = ?,
          format  = ?,
          user_id = ?
      where post_id = ?
    `;
    await mainDB.execute(sql, [post.title, post.content, post.format, post.userId, post.postId]);
  }

  async selectByUserId(userId: number): Promise<Post[]> {
    const sql = `
      select
        post_id as postId,
        title,
        content,
        format,
        user_id as userId,
        created_at as createdAt,
        updated_at as updatedAt
      from post
      where user_id = ?
    `;
    const rows = await mainDB.query(sql, [userId]);
    return rows.map((row) => this.mapToPost(row));
  }

  async countUserPosts(userId: number): Promise<number> {
    throw new Error('Method not implemented.');
  }

  async selectByPostId(postId: number): Promise<Post | undefined> {
    const sql = `
      select
        post_id as postId,
        title,
        content,
        format,
        user_id as userId,
        created_at as createdAt,
        updated_at as updatedAt
      from post
      where post_id = ?
    `;
    const row = await mainDB.queryOne(sql, [postId]);
    return !row ? undefined : this.mapToPost(row);
  }

  async insert(post: Post): Promise<PostId> {
    if (post.postId > 1) {
      throw boom.internal('cannot insert this post: ' + post.postId);
    }

    const sql = `
      insert into post
      set title   = ?,
          content = ?,
          format  = ?,
          user_id = ?
    `;
    return await mainDB.insertOneWithAutoIncrement(sql, [post.title, post.content, post.format, post.userId]);
  }

  private mapToPost(row: RowDataPacket): Post {
    return {
      userId: row['userId'],
      title: row['title'],
      postId: row['postId'],
      content: row['content'],
      format: row['format'],
    };
  }
}

export const createPostRepository = (): PostRepository => new PostMySQLRepository();
